﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;

namespace DataMappers
{
    public interface IDomainObject<IDType, DType> where DType : IDomainObject<IDType, DType>
    {
        void Remove();
        void Saved();
        IDType ID { get; set; }

    }

    public abstract class DomainObject<IDType, DType> : IDomainObject<IDType, DType> where DType : DomainObject<IDType, DType>
    {
        private IDType _id;
        private readonly IEntityMapper<IDType, DType> _mapper;

        #region StateObject

        protected interface State{
        State onRemove(); // object was removed
        State onWrite();  // object was written
        State onSave();   // object was saved by the unit of work     
        }
      
        protected class Clean : State{
            private readonly IEntityMapper<IDType, DType> _mapper;
            private DType _self;
            public Clean(IEntityMapper<IDType, DType> mapper, DType self){_mapper = mapper; _self = self;}
            public State onRemove() { _mapper.RemovedSet().Add(_self); return new ToRemove(_mapper, _self);}
            public State onSave() { throw new Exception();}
            public State onWrite() { _mapper.DirtySet().Add(_self); return new Dirty(_mapper, _self);}
        }
    
        protected class ToRemove : State{
            private readonly IEntityMapper<IDType, DType> _mapper;
            private DType _self;
            public ToRemove(IEntityMapper<IDType, DType> mapper, DType self){_mapper = mapper; _self = self;}
            public State onRemove() { return this;}        
            public State onSave() { _mapper.RemovedSet().Remove(_self); return new Removed(_mapper, _self);}
            public State onWrite() { throw new Exception();}
        }
    
        protected class Removed : State{        
            private readonly IEntityMapper<IDType, DType> _mapper;
            private DType _self;
            public Removed(IEntityMapper<IDType, DType> mapper, DType self) { _mapper = mapper; _self = self; }
            public State onRemove() { throw new Exception();}
            public State onSave() { throw new Exception(); }
            public State onWrite() { throw new Exception(); }
        }
    
        protected class Dirty : State{
            private readonly IEntityMapper<IDType, DType> _mapper;
            private DType _self;
            public Dirty(IEntityMapper<IDType, DType> mapper, DType self) { _mapper = mapper; _self = self; }
            public State onRemove() { _mapper.DirtySet().Remove(_self); _mapper.RemovedSet().Add(_self); return new ToRemove(_mapper, _self); }        
            public State onSave() { _mapper.DirtySet().Remove(_self); return new Clean(_mapper, _self);}
            public State onWrite() { return this;}
        }
    
        protected class New : State{
            private readonly IEntityMapper<IDType, DType> _mapper;
            private DType _self;
            public New(IEntityMapper<IDType, DType> mapper, DType self)
            { 
                _mapper = mapper;
                _self = self; 
                mapper.NewSet().Add(_self);
            }
            public State onRemove() { _mapper.NewSet().Remove(_self); return new Removed(_mapper, _self); }
            public State onSave() { _mapper.NewSet().Remove(_self); return new Clean(_mapper, _self); }
            public State onWrite() { return this;}
        }

        #endregion

        private State _state;
        protected abstract DType Self();

        protected void MarkDirty()
        {
            _state = _state.onWrite();
        }

        public void Remove()
        {
            _state = _state.onRemove();
        }

        public void Saved()
        {
            _state = _state.onSave();
        }

        
        //TODO add constructor and mapper
        public IDType ID
        {
            get { return _id; }
            set { _id = value; }
        }
        public DomainObject()
        {
            _mapper = UnitOfWork.Current.Get<IDType, DType>(Self());
            _state = new New(_mapper, Self());
        }

        public DomainObject(IDType id)
        {
            _id = id;
            _mapper = UnitOfWork.Current.Get<IDType, DType>(Self());
            _state = new Clean(_mapper, Self());

        }

    }

    public interface IFilter
    {
        String GetWhereClause();
        void BindValues(SqlCommand cmd);
    }

    public interface IValueHolder<K, T>
    {
        T Get();
        void Set(T elem);
    }

    public class ConcreteValueHolder<K, T> : IValueHolder<K,T> where T : IDomainObject<K,T> 
    {
        private readonly IEntityMapper<K, T> _mapper;
        private readonly K _key;
        private bool _loaded;
        private T _value;

        public ConcreteValueHolder() 
        {
            _loaded = true;
            _mapper = null;
            _key = default(K);
            
        }

        public ConcreteValueHolder(K key, IEntityMapper<K, T> mapper)
        {
            _mapper = mapper;
            _key = key;
            _loaded = false;
        }

        #region IValueHolder<K,T> Members

        public T Get()
        {
            //TODO - check better when Key is value type
            if (_key.Equals(default(K)) || _loaded) return _value;
            _value = _mapper.GetByID(_key);
            return _value;
        }

        public void Set(T elem)
        {
            _value = elem;
            _loaded = true;
        }

        #endregion
    }

    public class LazySet<K, T> : IEnumerable<T> where T : IDomainObject<K,T>
    {

        private IEntityMapper<K, T> _mapper;
        private HashSet<T> _set;
        private IFilter _filter;

        public LazySet(IEntityMapper<K, T> mapper, IFilter filter)
        {
            _mapper = mapper;
            _filter = filter;
        }



        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            if (_set == null)
            {
                _set = new HashSet<T>();
                _mapper.LoadFilteredInto(_set, _filter);
            }
            return _set.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            if (_set == null)
            {
                _set = new HashSet<T>();
                _mapper.LoadFilteredInto(_set, _filter);
            }
            return _set.GetEnumerator();
        }

        #endregion
    }
    public interface IEntityMapperForUpdates
    {
        void InsertNewObjects();
        void DeleteRemovedObjects();
        void UpdateDirtyObjects();
        void Refresh();
        void DeleteAllObjects();
    }
    public interface IEntityMapper<TKey, TEntity> : IEntityMapperForUpdates 
    {
        ISet<TEntity> NewSet();
        ISet<TEntity> CleanSet();
        ISet<TEntity> DirtySet();
        ISet<TEntity> RemovedSet();

        TEntity GetByID(TKey t);
 
        void LoadFilteredInto(ICollection<TEntity> col, IFilter filter);
        void LoadAllInto(ICollection<TEntity> col);

    //    void InsertNewObjects();
        void Insert(TEntity t);
      //  void DeleteRemovedObjects();
        void Delete(TEntity t);
      //  void UpdateDirtyObjects();
        void Update(TEntity t);

        void SetConnector(Connection conn);

        

    }
}